home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / MiscKit1.7.1 / MiscKit / Palettes / MiscSoundPalette / MiscSoundUtil.subproj / MiscSoundView.m < prev    next >
Encoding:
Text File  |  1995-03-25  |  26.1 KB  |  1,059 lines

  1. /*
  2.  
  3. MiscSoundView
  4. Version 1.2
  5. Copyright (c) 1995 by Sean Luke
  6. Donated to the MiscKit
  7.  
  8. Permission to use, copy, modify, and distribute this material 
  9. for any purpose and without fee, under the restrictions as noted 
  10. in the MiscKit copyright notice, is hereby granted, provided that
  11. the MiscKit copyright notice and this permission notice 
  12. appear in all source copies, and that the author's name shall not
  13. be used in advertising or publicity pertaining to this 
  14. material without the specific, prior written permission 
  15. of the author.  SEAN O. LUKE  MAKES NO REPRESENTATIONS ABOUT THE
  16. ACCURACY OR SUITABILITY OF THIS MATERIAL FOR ANY PURPOSE.  
  17. IT IS PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
  18.  
  19. */
  20.  
  21. // Known Bugs:
  22.  
  23. //    1)  Play Mark doesn't disappear unless you tell it to.
  24. //    2)    Rulers don't appear until explicitly told to.
  25. //    3)    Ruler values slowly drift (rounding error)
  26.  
  27. #import "MiscSoundView.h"
  28. #import <math.h>
  29. #import <stdio.h>
  30.  
  31. // CONVENIENCE FUNCTIONS
  32.  
  33. double scroll_to_reduction (double scrollValue, double ratio)
  34.     // scrollValue must be a number between 1 and 0
  35.     // ratio is sample count / display units
  36.     // returns -1 if error
  37.     // this function considers roundoff to integers,
  38.     // and a maximum value at integer point
  39.     {
  40.     if (scrollValue>1.0||scrollValue<0.0) 
  41.         {
  42.         printf ("scroll_to_reduction ERROR:  scrollValue is %f, ratio is%f\n", scrollValue, ratio);
  43.         return -1.0;
  44.         }
  45.     return ceil(pow(ceil(ratio),scrollValue));
  46.     }
  47.  
  48.     
  49. double reduction_to_scroll (double reduction, double ratio)    
  50.     // reduction must be a number above 1, preferably < ratio
  51.     // ratio is sample count / display units
  52.     // returns -1 if error
  53.     // this function does not consider roundoff to integers...
  54.     // but does consider a maximum value at integer point
  55.     {
  56.     if (reduction<1.0) 
  57.         {
  58.         printf ("reduction_to_scroll ERROR:  reduction is %f, ratio is %f\n", reduction, ratio);
  59.         return -1.0;
  60.         }
  61.     return log(reduction)/log(ceil(ratio));
  62.     }
  63.     
  64.     
  65. // private function that rounds x to the nearest integer.
  66.  
  67. int MISCSOUNDVIEW_round(float x)
  68.     {
  69.     if (fabs(ceil(x)-x)<=0.5) return (int) ceil(x);
  70.     return (int) floor(x);
  71.     }
  72.  
  73.  
  74. @implementation MiscSoundView
  75.  
  76. // Private method that tells the SoundView to wipe itself clean when running
  77.  
  78. - _wipeClean
  79.     {
  80.     wipe_clean=YES;
  81.     return self;
  82.     }
  83.  
  84.  
  85. // Private method that modify the bounds/frame rect...
  86.  
  87. - _adjustBounds
  88. {
  89.     // This assumes that MiscSoundView is in a scrollView!
  90.     // It also does _not_ draw the MiscSoundView...
  91.     
  92.     id scroll_view;
  93.     NXSize ThisSize;
  94.     
  95.     if ((scroll_view=[[self superview] superview])==NULL) 
  96.         {
  97.         return NULL;        // not in scrollView!
  98.         }
  99.     
  100.     [scroll_view getContentSize: &ThisSize];    
  101.     [super sizeTo: bounds.size.width : 
  102.         ThisSize.height- (display_x_axis_marks ? MISCSOUNDVIEW_RULER_HEIGHT +
  103.             MISCSOUNDVIEW_PLAY_MARK_HEIGHT : 0)];    
  104.     return self;
  105. }
  106.  
  107. - adjustBounds:sender
  108. {    
  109.     if ([self _adjustBounds]==NULL) {return NULL;}
  110.     [self _wipeClean];
  111.     [self display];
  112.     return self;
  113. }
  114.  
  115. - sizeTo:(NXCoord)width :(NXCoord)height
  116.     {
  117.     id returnval=[super sizeTo:width:height];
  118.     [self _adjustBounds];
  119.     [self _wipeClean];
  120.     return returnval;
  121.     }
  122.  
  123.  
  124. - initFrame:(const NXRect*) frameRect
  125. {
  126.     id returnval=[super initFrame:frameRect];
  127.     display_x_axis_marks=NO;
  128.     display_y_axis_grid=NO;
  129.     display_zero_line=NO;
  130.     display_labels=YES;
  131.     y_display_format=MISCSOUNDVIEW_YAXIS_DISPLAY_FORMAT_SIXTEEN;
  132.     minor_tick_spacing=1000.0;
  133.     minor_tick_spacing_format=MISCSOUNDVIEW_XAXIS_SPACING_FORMAT_SAMPLES;
  134.     major_tick_spacing=10;
  135.     old_play_mark=-1;
  136.     play_mark=-1;
  137.     scroll_to_reflect_playing=NO;
  138.     only_change_play_mark=NO;
  139.     [self _wipeClean];
  140.     [self _adjustBounds];
  141.     return returnval;
  142. }
  143.  
  144.  
  145. - drawSelf:(const NXRect *)rects :(int)rectCount
  146. {
  147.  
  148.  
  149. //-------------------------------------
  150. //    Some DrawSelf Free-Form Poetry:
  151. //
  152. //    drawSelf is probably
  153. //    the most evil monolithic Obj-C ever
  154. //    created.  I can't bother
  155. //    prettifying it, though I might
  156. //    make it
  157. //    barely understandable.
  158. //-------------------------------------
  159.  
  160.  
  161.  
  162.     NXSize temp;
  163.     BOOL display_minor_ticks;
  164.     float maxval,lastval;
  165.     int a;
  166.     int numberOfSamples;
  167.     float tick_height;
  168.     int startsample,endsample,xx;
  169.     id returnval=self;
  170.     int true_sample_count=[sound sampleCount];
  171.         
  172.     // Draw SoundView stuff
  173.     if (!only_change_play_mark)
  174.         {
  175.         [self setClipping:YES];
  176.         returnval=[super drawSelf: rects: rectCount];
  177.         }
  178.     [self setClipping:NO];
  179.  
  180.     /*     
  181.      *
  182.      *
  183.      * Drawing the Y-Axis elements (Amplitude and Zero Line)...
  184.      *
  185.      *
  186.      *
  187.      */
  188.     
  189.     if (display_y_axis_grid&&!only_change_play_mark&&true_sample_count)
  190.         {
  191.         if (y_display_format==MISCSOUNDVIEW_YAXIS_DISPLAY_FORMAT_DECIBEL)
  192.             {
  193.             maxval=SNDConvertDecibelsToLinear(100.0);
  194.             lastval=bounds.size.height/2;
  195.             
  196.             for (a=19;a>=10;a--)
  197.                 {
  198.                 temp.height=lastval-SNDConvertDecibelsToLinear
  199.                     ((float)a*5)/maxval*(bounds.size.height/2);
  200.                 temp.width=0;
  201.                 [self convertSize:&temp toView:nil];
  202.                 if (temp.height>2.0)
  203.                     {
  204.                     PSsetlinewidth(0);
  205.                     PSsetgray(NX_DKGRAY);
  206.                     PSmoveto (rects[0].origin.x,SNDConvertDecibelsToLinear
  207.                         ((float)a*5)/maxval*(bounds.size.height/2));
  208.                     PSrlineto(rects[0].size.width,0);
  209.                     PSsetlinewidth(0);
  210.                     PSmoveto (rects[0].origin.x,0-SNDConvertDecibelsToLinear
  211.                         ((float)a*5)/maxval*(bounds.size.height/2));
  212.                     PSrlineto(rects[0].size.width,0);
  213.                     PSstroke();
  214.                     }
  215.                 lastval=SNDConvertDecibelsToLinear
  216.                     ((float)a*5)/maxval*(bounds.size.height/2);
  217.                 }
  218.             }
  219.         else if (y_display_format==MISCSOUNDVIEW_YAXIS_DISPLAY_FORMAT_SIXTEEN)
  220.             {
  221.             for (a=1;a<8;a++)
  222.                 {
  223.                 PSsetlinewidth(0);
  224.                 PSsetgray(NX_DKGRAY);
  225.                 PSmoveto (rects[0].origin.x,(bounds.size.height/2)/8*a);
  226.                 PSrlineto(rects[0].size.width,0);
  227.                 PSsetlinewidth(0);
  228.                 PSmoveto (rects[0].origin.x,0-(bounds.size.height/2)/8*a);
  229.                 PSrlineto(rects[0].size.width,0);
  230.                 PSstroke();    
  231.                 }        
  232.             }
  233.         else if (y_display_format==MISCSOUNDVIEW_YAXIS_DISPLAY_FORMAT_TWENTY)
  234.             {
  235.             for (a=1;a<10;a++)
  236.                 {
  237.                 PSsetlinewidth(0);
  238.                 PSsetgray(NX_DKGRAY);
  239.                 PSmoveto (rects[0].origin.x,(bounds.size.height/2)/10*a);
  240.                 PSrlineto(rects[0].size.width,0);
  241.                 PSsetlinewidth(0);
  242.                 PSmoveto (rects[0].origin.x,0-(bounds.size.height/2)/10*a);
  243.                 PSrlineto(rects[0].size.width,0);
  244.                 PSstroke();    
  245.                 }        
  246.             }
  247.         else // y_display_format==MISCSOUNDVIEW_YAXIS_DISPLAY_FORMAT_TWENTYFOUR
  248.             {
  249.             for (a=1;a<12;a++)
  250.                 {
  251.                 PSsetlinewidth(0);
  252.                 PSsetgray(NX_DKGRAY);
  253.                 PSmoveto (rects[0].origin.x,(bounds.size.height/2)/12*a);
  254.                 PSrlineto(rects[0].size.width,0);
  255.                 PSsetlinewidth(0);
  256.                 PSmoveto (rects[0].origin.x,0-(bounds.size.height/2)/12*a);
  257.                 PSrlineto(rects[0].size.width,0);
  258.                 PSstroke();    
  259.                 }        
  260.             }
  261.         }
  262.     if (display_zero_line&&!only_change_play_mark&&true_sample_count)
  263.         {
  264.         PSsetlinewidth(0);
  265.         PSsetgray(NX_DKGRAY);
  266.         PSmoveto (rects[0].origin.x,0);
  267.         PSrlineto(rects[0].size.width,0);
  268.         PSstroke();
  269.         }
  270.  
  271.  
  272.  
  273.     /*     
  274.      *
  275.      *
  276.      * Drawing the X-Axis elements (Ruler and Play-Mark)...
  277.      *
  278.      *
  279.      *
  280.      */
  281.         
  282.         
  283.     if (display_x_axis_marks&&(minor_tick_spacing!=0.0)&&major_tick_spacing)                
  284.     
  285.     
  286.         // display the axis
  287.         
  288.         {
  289.         double scale_factor;
  290.         NXRect temprect;
  291.         char tempstring[20];
  292.         float print_value;
  293.         BOOL display_major_ticks;
  294.         BOOL enough_room_for_labels;
  295.         
  296.         // to begin, figure out the scaling factor:
  297.         
  298.         scale_factor=bounds.size.height/frame.size.height;
  299.         
  300.         // then draw ticks
  301.         
  302.         tick_height=MISCSOUNDVIEW_RULER_HEIGHT/2;
  303.  
  304.         if  (minor_tick_spacing_format==
  305.                         MISCSOUNDVIEW_XAXIS_SPACING_FORMAT_SAMPLES)
  306.                                 // display in samples
  307.             {
  308.             numberOfSamples=(int)minor_tick_spacing;
  309.             }
  310.         else if (minor_tick_spacing_format==
  311.                         MISCSOUNDVIEW_XAXIS_SPACING_FORMAT_PERCENT)
  312.                                 // display in percentage
  313.             {
  314.             numberOfSamples=(int)
  315.                         (minor_tick_spacing/100.0*[sound sampleCount]);
  316.             }
  317.         else                    // display in seconds
  318.             {
  319.             numberOfSamples=(int)(minor_tick_spacing*[sound samplingRate]);
  320.             }
  321.         
  322.         if (!numberOfSamples) numberOfSamples=1;        // for divide-by-zero    
  323.         
  324.         startsample=(((int) (rects[0].origin.x*reductionFactor))
  325.                             /numberOfSamples)*numberOfSamples;
  326.             
  327.                 // note the integer division above!!!!  This
  328.                 // does automatic rounding properly...
  329.         
  330.         display_minor_ticks=(BOOL)(((float)numberOfSamples)/reductionFactor>=
  331.             MISCSOUNDVIEW_TICK_MINIMUM_SPACING);
  332.         display_major_ticks=(BOOL)(((float)numberOfSamples)
  333.             *major_tick_spacing/reductionFactor>=
  334.             MISCSOUNDVIEW_TICK_MINIMUM_SPACING);
  335.         enough_room_for_labels=(BOOL)(((float)numberOfSamples)
  336.             *major_tick_spacing/reductionFactor>=
  337.             MISCSOUNDVIEW_TICK_MINIMUM_SPACING_WITH_LABELS);
  338.  
  339.         endsample=((int)(rects[0].size.width+rects[0].origin.x))
  340.             *reductionFactor;
  341.         
  342.         
  343.         
  344.         // Erase ruler if needed...
  345.         
  346.         if (!only_change_play_mark&&true_sample_count)
  347.         {
  348.             // wipe the ruler clean if needed
  349.     
  350.             if (wipe_clean)
  351.                 {
  352.                 PSsetgray(NX_WHITE);
  353.                 temprect.origin.x=rects[0].origin.x;
  354.                 temprect.origin.y=bounds.size.height/2;
  355.                 temprect.size.width=rects[0].size.width;
  356.                 temprect.size.height=
  357.                     (MISCSOUNDVIEW_RULER_HEIGHT+MISCSOUNDVIEW_PLAY_MARK_HEIGHT)
  358.                     *scale_factor;
  359.                 PSrectfill(temprect.origin.x,temprect.origin.y,
  360.                     temprect.size.width,temprect.size.height);
  361.                 wipe_clean=NO;        // finished
  362.                 }
  363.     
  364.             // Draw ruler lines
  365.         
  366.             PSsetlinewidth(0);
  367.             PSsetgray(NX_BLACK);
  368.             PSmoveto(rects[0].origin.x, bounds.size.height/2);
  369.             PSrlineto(rects[0].size.width, 0);
  370.             PSmoveto(rects[0].origin.x,bounds.size.height/2+
  371.                 MISCSOUNDVIEW_PLAY_MARK_HEIGHT*scale_factor);
  372.             PSrlineto(rects[0].size.width,0);
  373.         }
  374.         PSstroke();
  375.         
  376.         
  377.         // Next, erase old play mark (if any) and draw new play-mark
  378.         
  379.         if (old_play_mark!=-1)
  380.             {
  381.             PSsetgray(NX_WHITE);
  382.             PSmoveto(old_play_mark/reductionFactor+bounds.origin.x,
  383.                 bounds.size.height/2+1*scale_factor);
  384.             PSrlineto(0,2*scale_factor);
  385.             PSrmoveto(-1,-1*scale_factor);
  386.             PSrlineto(2,0);
  387.             }
  388.         PSstroke();
  389.         old_play_mark=play_mark;
  390.         
  391.         if (old_play_mark!=-1&&true_sample_count)    // don't draw if no samples
  392.             {
  393.             PSsetgray(NX_BLACK);
  394.             PSmoveto(old_play_mark/reductionFactor+bounds.origin.x,
  395.                 bounds.size.height/2+1*scale_factor);
  396.             PSrlineto(0,2*scale_factor);
  397.             PSrmoveto(-1,-1*scale_factor);
  398.             PSrlineto(2,0);
  399.             }
  400.         PSstroke();
  401.  
  402.  
  403.  
  404.         // finally, draw the ticks and add the labels
  405.  
  406.         if (!only_change_play_mark&&true_sample_count)
  407.         {
  408.             // here we draw the label equal to or directly to the left of
  409.             // startsample, to fix a little bug in drawing that comes up 
  410.             // when scrolling.
  411.             
  412.             xx=(startsample/(numberOfSamples*major_tick_spacing))        
  413.                     *(numberOfSamples*major_tick_spacing);
  414.                                 // note integer division!!!
  415.             
  416.             PSmoveto(((float)xx)/reductionFactor+bounds.origin.x,
  417.                 bounds.size.height/2+(1+MISCSOUNDVIEW_PLAY_MARK_HEIGHT
  418.                 *scale_factor));
  419.                         // should the 1 be modified by the scale factor?
  420.             
  421.             PSrmoveto(0,tick_height*scale_factor);
  422.             if (display_labels&&enough_room_for_labels)
  423.                 {
  424.                 if (minor_tick_spacing_format==
  425.                     MISCSOUNDVIEW_XAXIS_SPACING_FORMAT_SAMPLES)
  426.                     print_value=(float)xx;
  427.                 else if (minor_tick_spacing_format==
  428.                     MISCSOUNDVIEW_XAXIS_SPACING_FORMAT_PERCENT)
  429.                     print_value=((float)xx)/((float)[sound sampleCount])*100;
  430.                 else print_value=((float)xx)/(float)[sound samplingRate];
  431.                 PSgsave();
  432.                 PSinitmatrix();
  433.                 PSrmoveto(0,1);
  434.                 if (minor_tick_spacing_format==
  435.                     MISCSOUNDVIEW_XAXIS_SPACING_FORMAT_SAMPLES)
  436.                     sprintf(tempstring, " %d", (int)print_value);
  437.                             // use int value
  438.                             
  439.                 else if (minor_tick_spacing_format==
  440.                     MISCSOUNDVIEW_XAXIS_SPACING_FORMAT_PERCENT)
  441.                     sprintf(tempstring, " %d",
  442.                     MISCSOUNDVIEW_round(print_value));
  443.                 else sprintf(tempstring, " %.3f", print_value);            
  444.                             // use float value
  445.                             
  446.                 PSselectfont("Helvetica",8);
  447.                 PSshow(tempstring);
  448.                 PSgrestore();
  449.                 }
  450.                 
  451.             // end bug fix.  We continue, now drawing the ticks 
  452.             // and adding labels...
  453.  
  454.         
  455.             if (minor_tick_spacing!=0.0) 
  456.             for (xx=startsample;xx<=endsample;xx+=numberOfSamples)
  457.                 {
  458.                 // Prepare the ruler for labelling
  459.                 
  460.                 if (display_major_ticks&&major_tick_spacing&&
  461.                     (!(xx%(numberOfSamples*major_tick_spacing))))
  462.                     {
  463.                     PSsetgray(NX_BLACK);
  464.                     PSmoveto(((float)xx)/reductionFactor+bounds.origin.x,
  465.                             bounds.size.height/2+
  466.                             (1+MISCSOUNDVIEW_PLAY_MARK_HEIGHT*scale_factor));
  467.                     PSrlineto(0,tick_height*scale_factor);
  468.                     PSgsave();
  469.                     PSstroke();
  470.                     PSgrestore();    // preserves path and (more importantly)
  471.                                     // currentpoint
  472.                     
  473.                     // And Add Label
  474.                     
  475.                     if (display_labels&&enough_room_for_labels)
  476.                         {
  477.                         if (minor_tick_spacing_format==
  478.                             MISCSOUNDVIEW_XAXIS_SPACING_FORMAT_SAMPLES)
  479.                                                 // display in samples
  480.                                                 
  481.                             print_value=(float)xx;
  482.                         else if (minor_tick_spacing_format==
  483.                             MISCSOUNDVIEW_XAXIS_SPACING_FORMAT_PERCENT)
  484.                                                 // display in percentage
  485.                                                 
  486.                             print_value=((float)xx)/
  487.                                 ((float)[sound sampleCount])*100;
  488.                                 
  489.                         else                    // display in seconds
  490.                             print_value=((float)xx)/(float)
  491.                                 [sound samplingRate];    
  492.                         PSgsave();
  493.                         PSinitmatrix();
  494.                         PSrmoveto(0,1);
  495.                         if (minor_tick_spacing_format==
  496.                             MISCSOUNDVIEW_XAXIS_SPACING_FORMAT_SAMPLES)
  497.                             sprintf(tempstring, " %d", (int)print_value);        
  498.                                     // use int value
  499.                                     
  500.                         else if (minor_tick_spacing_format==
  501.                             MISCSOUNDVIEW_XAXIS_SPACING_FORMAT_PERCENT)
  502.                             sprintf(tempstring, " %d", 
  503.                                 MISCSOUNDVIEW_round(print_value));
  504.                                     // use rounded int value
  505.                                     
  506.                         else sprintf(tempstring, " %.3f", print_value);    
  507.                                     // use float value
  508.                                     
  509.                         PSselectfont("Helvetica",8);
  510.                         PSshow(tempstring);
  511.                         PSgrestore();
  512.                         }
  513.                     }
  514.                 else
  515.                     {
  516.                     if (display_minor_ticks)
  517.                         {
  518.                         PSsetgray(NX_LTGRAY);
  519.                         PSmoveto(((float)xx)/reductionFactor+
  520.                             bounds.origin.x,bounds.size.height/2+
  521.                             (1+MISCSOUNDVIEW_PLAY_MARK_HEIGHT*scale_factor));
  522.                         PSrlineto(0,tick_height/2*scale_factor);                    
  523.                                 // minor ticks are 1/2 height of major ticks
  524.                                 
  525.                         PSstroke();
  526.                         }
  527.                     }
  528.                 PSnewpath(); // deletes path saved when drawing major ticks.    
  529.                 }
  530.             }        // play-mark bracket (see above)
  531.         }
  532.  
  533.     // Return to normal...
  534.     [self setClipping:YES];    
  535.     return returnval;
  536. }
  537.  
  538.  
  539.  
  540. - getSelection:(int *)firstSample size:(int *)sampleCount
  541. // a fixed version of getSelection which returns the proper selected area
  542. // POSSIBLE BUG ALERT:  NeXT's stuff may depend on the broken getSelection!
  543.  
  544. {
  545.     int samples=[[self sound] sampleCount];
  546.     [super getSelection:firstSample size:sampleCount];
  547.     // Now we check and modify accordingly:
  548.     if (*firstSample+*sampleCount>samples) *sampleCount=samples-*firstSample;
  549.     return self;
  550. }
  551.  
  552.  
  553.  
  554. - setSelection:(int)firstSample size:(int)sampleCount
  555. // a fixed version of setSelection which sets the proper selected area
  556. // POSSIBLE BUG ALERT:  NeXT's stuff may depend on the broken setSelection!
  557.  
  558. {
  559.     int samples=[[self sound] sampleCount];
  560.     if (firstSample+sampleCount>=samples)
  561.         // we make certain that the right amount is selected!
  562.         // Note that you can't just set the selection beyond the sound
  563.         // value, because the soundView doesn't allow it.
  564.         // so here we physically set the soundView's selection rect...
  565.         // hope this doesn't cause weird bugs...
  566.         {
  567.         [super setSelection:firstSample size:sampleCount];    
  568.             // as good as can be done...
  569.         selectionRect.size.width*=1.1;    
  570.             // this makes the selection a tiny bit larger
  571.         }
  572.     else [super setSelection:firstSample size:sampleCount];
  573.     return self;
  574. }
  575.  
  576.  
  577.  
  578. - selectAll:sender
  579. // a fixed version of selectAll which sets the proper selected area
  580. // POSSIBLE BUG ALERT:  NeXT's stuff may depend on the broken selectAll!
  581.  
  582. {
  583.     [self setSelection:0 size:[[self sound] sampleCount]];
  584.     return self;
  585. }
  586.  
  587.  
  588. - set:
  589.     (BOOL) displayXAxis:
  590.     (BOOL) displayYAxis:
  591.     (BOOL) displayLabels:
  592.     (BOOL) displayZeroLine:
  593.     (int) majorTickSpacing:
  594.     (float) minorTickSpacing:
  595.     (int) minorTickSpacingFormat:
  596.     (int) yDisplayFormat:
  597.     (BOOL) scrollToReflectPlaying
  598.     
  599.     // Sets these values...
  600.     
  601.     
  602.     {
  603.     display_x_axis_marks=displayXAxis;
  604.     display_y_axis_grid=displayYAxis;
  605.     display_labels=displayLabels;
  606.     display_zero_line=displayZeroLine;
  607.     scroll_to_reflect_playing=scrollToReflectPlaying;
  608.     
  609.     // first set the default spacing in case this spacing is invalid
  610.     major_tick_spacing=5;
  611.     if (minorTickSpacingFormat==
  612.         MISCSOUNDVIEW_XAXIS_SPACING_FORMAT_PERCENT) minor_tick_spacing=10.0;
  613.     else if (minorTickSpacingFormat==
  614.         MISCSOUNDVIEW_XAXIS_SPACING_FORMAT_SAMPLES) minor_tick_spacing=1000;
  615.     else if (minorTickSpacingFormat==
  616.         MISCSOUNDVIEW_XAXIS_SPACING_FORMAT_SECONDS) minor_tick_spacing=1;
  617.     
  618.     // then change to the user's spacing if his spacing is acceptable
  619.     if (majorTickSpacing>0)
  620.         major_tick_spacing=majorTickSpacing;
  621.     if ((minorTickSpacing>0.0&&
  622.         (minorTickSpacingFormat!=
  623.         MISCSOUNDVIEW_XAXIS_SPACING_FORMAT_SAMPLES)) ||
  624.         (minorTickSpacing>=1.0&&
  625.         (minorTickSpacingFormat==
  626.         MISCSOUNDVIEW_XAXIS_SPACING_FORMAT_SAMPLES)))
  627.         minor_tick_spacing=minorTickSpacing;
  628.     
  629.     minor_tick_spacing_format=minorTickSpacingFormat;
  630.     y_display_format=yDisplayFormat;
  631.     [self _adjustBounds];
  632.     
  633.     return self;
  634.     }
  635.     
  636. - (BOOL) xAxisDisplayed
  637.     {
  638.     return display_x_axis_marks;
  639.     }
  640.  
  641. - (BOOL) yAxisDisplayed
  642.     {
  643.     return display_y_axis_grid;
  644.     }
  645.  
  646. - (BOOL) zeroLineDisplayed
  647.     {
  648.     return display_zero_line;
  649.     }
  650.  
  651. - (BOOL) labelsDisplayed
  652.     {
  653.     return display_labels;
  654.     }
  655.  
  656. - (BOOL) scrollToReflectPlaying
  657.     {
  658.     return scroll_to_reflect_playing;
  659.     }
  660.  
  661. - (int) majorTickSpacing
  662.     {
  663.     return major_tick_spacing;
  664.     }
  665.  
  666. - (float) minorTickSpacing
  667.     {
  668.     return minor_tick_spacing;
  669.     }
  670.  
  671. - (int) minorTickSpacingFormat
  672.     {
  673.     return minor_tick_spacing_format;
  674.     }
  675.  
  676. - (int) yDisplayFormat
  677.     {
  678.     return y_display_format;
  679.     }
  680.  
  681.  
  682. - scrollToSelection:sender
  683.     // scrolls if there's room to do so
  684.     {
  685.     int first_sample,number_of_samples;
  686.     int total_samples;
  687.     NXRect bounds_rect;
  688.     NXRect visible_rect;
  689.     NXPoint scroll_point;
  690.     id currentScrollView=[[self superview] superview];
  691.     
  692.     if (currentScrollView==NULL) return NULL;        // not in scrollView!
  693.     
  694.     total_samples=[[self sound] sampleCount];
  695.     [self getSelection:&first_sample size:&number_of_samples];
  696.     [self getBounds:&bounds_rect];        // get new bounds information if any
  697.     [self getVisibleRect:&visible_rect];
  698.     
  699.     if (!total_samples) total_samples=1;        // kills divide-by-zero
  700.     
  701.         scroll_point.x= ((bounds_rect.size.width*
  702.         (double)first_sample)/(double) total_samples);
  703.     scroll_point.y= (bounds_rect.origin.y);
  704.     if (scroll_point.x+visible_rect.size.width>
  705.         bounds.size.width) 
  706.             scroll_point.x=bounds.size.width-visible_rect.size.width;
  707.             
  708.     [self scrollPoint: &scroll_point];
  709.     [currentScrollView reflectScroll: [self superview]];
  710.     return self;
  711.     }
  712.     
  713. - scrollToSample:(int) samp
  714.     // scrolls to samp if there's room to do so.
  715.     {
  716.     int first_sample,number_of_samples;
  717.     int total_samples;
  718.     NXRect bounds_rect;
  719.     NXRect visible_rect;
  720.     NXPoint scroll_point;
  721.     id currentScrollView=[[self superview] superview];
  722.     
  723.     if (currentScrollView==NULL) return NULL;        // not in scrollView!
  724.     
  725.     total_samples=[[self sound] sampleCount];
  726.     first_sample=samp;
  727.     number_of_samples=0;
  728.     [self getBounds:&bounds_rect];        // get new bounds information if any
  729.     [self getVisibleRect:&visible_rect];
  730.     
  731.     if (!total_samples) total_samples=1;        // kills divide-by-zero
  732.     
  733.     scroll_point.x= ((bounds_rect.size.width*
  734.         (double)first_sample)/(double) total_samples);
  735.     scroll_point.y= (bounds_rect.origin.y);
  736.     if (scroll_point.x+visible_rect.size.width>
  737.         bounds.size.width) 
  738.             scroll_point.x=bounds.size.width-visible_rect.size.width;
  739.             
  740.     [self scrollPoint: &scroll_point];
  741.     [currentScrollView reflectScroll: [self superview]];
  742.     return self;
  743.     }
  744.     
  745. - (int)scrollSample
  746.     // returns the current scrolled sample (leftmost)
  747.     {
  748.     int firstViewSample;
  749.     NXRect visibleRect;
  750.     double ReductionFactor;
  751.     
  752.     ReductionFactor= (double) [self reductionFactor];
  753.     [self getVisibleRect: &visibleRect];
  754.     firstViewSample=(int)(visibleRect.origin.x*ReductionFactor);
  755.     if (sound)
  756.         if (firstViewSample>=[sound sampleCount]) 
  757.             firstViewSample=[sound sampleCount]-1;
  758.     if (firstViewSample<0) firstViewSample=0;
  759.     return firstViewSample;
  760.     }
  761.  
  762. - setPlayMark:(int)sample
  763.     // -1 is default position
  764.     {
  765.     old_play_mark=play_mark;
  766.     play_mark=(float)sample;
  767.     if (display_x_axis_marks&&(minor_tick_spacing!=0.0)&&major_tick_spacing)
  768.         // we're showing ticks...
  769.     only_change_play_mark=YES;
  770.     [self display];
  771.     only_change_play_mark=NO;
  772.     return self;
  773.     }
  774.     
  775.     
  776.     
  777.  
  778. - (double) _scrollValue                            // a private method
  779.     {
  780.     NXSize content_size;
  781.     id currentScrollView;
  782.     currentScrollView=[[self superview] superview];
  783.     
  784.     if (currentScrollView==NULL) return 0;        // not in scrollView!
  785.     
  786.     [currentScrollView getContentSize:&content_size];
  787.     
  788.     return reduction_to_scroll
  789.         ( [self reductionFactor],
  790.           ((double)[sound sampleCount])
  791.           /(double)content_size.width);
  792.     }
  793.     
  794.  
  795.  
  796. - _setScrollValue:(double) this_value        
  797.                     // private method. this_value must be between 1 and 0
  798.     {
  799.     NXSize content_size;
  800.     double new_reduction_factor;
  801.     int first_sample, number_of_samples;
  802.     id currentScrollView=[[self superview] superview];
  803.     
  804.     if (currentScrollView==NULL) return NULL;        // not in scrollView!
  805.     
  806.     [currentScrollView getContentSize:&content_size];
  807.  
  808.     [self getSelection:&first_sample size:&number_of_samples];
  809.  
  810.     new_reduction_factor= scroll_to_reduction        
  811.                     // is this where the problem lies?
  812.         ( this_value,
  813.           ((double)[sound sampleCount])
  814.           /(double)content_size.width);
  815.     
  816.     if (new_reduction_factor!=-1.0)        // it's not an error
  817.         {
  818.         [self setReductionFactor:new_reduction_factor];
  819.         [self setSelection:first_sample size: number_of_samples];
  820.         return self;
  821.         }
  822.     else
  823.         {
  824.         [self setSelection:first_sample size: number_of_samples];
  825.         return NULL;
  826.         }
  827.     }
  828.     
  829.     
  830.     
  831. - _zoomTo:(double) scroll_value                    // a private method
  832.     {
  833.     id returnval;
  834.     int first_sample, number_of_samples;
  835.     [window disableFlushWindow];
  836.     [self getSelection:&first_sample size:&number_of_samples];
  837.         
  838.     if (scroll_value>1.0) scroll_value=1.0;
  839.     if (scroll_value<0.0) scroll_value=0.0;
  840.     returnval=[self _setScrollValue:scroll_value];
  841.     [self setSelection:first_sample size: number_of_samples];
  842.     [self display];
  843.     [window reenableFlushWindow];
  844.     [window flushWindowIfNeeded];
  845.     return returnval;
  846.     }
  847.     
  848. - getZoomValueFrom:sender
  849.     {
  850.     return [self _zoomTo:[sender floatValue]];
  851.     }
  852.         
  853. - takeIntValueFrom:sender
  854.     {
  855.     int temp_play_mark=[sender intValue];
  856.     id snd=[self sound];
  857.     int samples;
  858.     
  859.     if (snd==NULL) samples=0;
  860.     else samples=[snd sampleCount];
  861.     
  862.     if (temp_play_mark>=0&&
  863.         temp_play_mark<samples&&scroll_to_reflect_playing)
  864.         {
  865.         [self scrollToSample:temp_play_mark];
  866.         [self setPlayMark:temp_play_mark];
  867.         }
  868.     else [self setPlayMark:-1];
  869.     return self;
  870.     }    
  871.     
  872. - zoomAllIn:this_soundview
  873.     {
  874.     [self _zoomTo:0.0];
  875.     [self scrollToSelection:self];
  876.     return self;
  877.     }
  878.     
  879.     
  880. - zoomAllOut:this_soundview
  881.     {
  882.     [self _zoomTo:1.0];
  883.     [self scrollToSelection:self];
  884.     return self;
  885.     }
  886.     
  887.  
  888.  
  889. - zoomOutOneReduction:sender
  890.     {
  891.     id returnval=self;
  892.     double scroll_value,reduction;
  893.     int first_sample, number_of_samples;
  894.     
  895.     [window disableFlushWindow];
  896.     [self getSelection:&first_sample size:&number_of_samples];
  897.     reduction=[self reductionFactor];
  898.     [self setReductionFactor:(int)reduction+1];
  899.     scroll_value=[self _scrollValue];
  900.     
  901.     if (scroll_value>1.0||scroll_value<0.0)    
  902.         {
  903.         if (scroll_value>1.0) scroll_value=1.0;
  904.         if (scroll_value<0.0) scroll_value=0.0;
  905.         returnval=[self _setScrollValue:scroll_value];
  906.         }
  907.     [self setSelection:first_sample size: number_of_samples];
  908.     [self scrollToSelection:self];
  909.     [window reenableFlushWindow];
  910.     [window flushWindowIfNeeded];
  911.     return returnval;
  912.     }
  913.  
  914.  
  915. - zoomInOneReduction:sender
  916.     {
  917.     id returnval=self;
  918.     double scroll_value;
  919.     double reduction;
  920.     int first_sample, number_of_samples;
  921.     
  922.     [window disableFlushWindow];
  923.     [self getSelection:&first_sample size:&number_of_samples];
  924.     reduction=[self reductionFactor];
  925.     if (reduction>=1.0) [self setReductionFactor:(int)reduction-1];
  926.     scroll_value=[self _scrollValue];
  927.     if (scroll_value>1.0||scroll_value<0.0)    
  928.         {
  929.         if (scroll_value>1.0) scroll_value=1.0;
  930.         if (scroll_value<0.0) scroll_value=0.0;
  931.         returnval=[self _setScrollValue:scroll_value];
  932.         }
  933.     [self setSelection:first_sample size: number_of_samples];
  934.     [self scrollToSelection:self];
  935.     [window reenableFlushWindow];
  936.     [window flushWindowIfNeeded];
  937.     return returnval;
  938.     }
  939.  
  940.  
  941.  
  942. - zoomToSelection:sender
  943.     {
  944.     int first_sample, number_of_samples, total_samples;
  945.     double reduction_factor;
  946.     NXRect visible_rect;
  947.     
  948.     // Step 0:  Get preliminary information
  949.     
  950.     if (self==NULL) return NULL;
  951.     [window disableFlushWindow];    
  952.     
  953.     total_samples=[sound sampleCount];
  954.     [self getSelection:&first_sample size:&number_of_samples];
  955.     
  956.     [self getVisibleRect:&visible_rect];
  957.  
  958.     // Step 1:  Zoom to the right reduction
  959.     
  960.     reduction_factor=MISCSOUNDVIEW_round(
  961.             ((double) number_of_samples) / ((double) visible_rect.size.width));
  962.     if (reduction_factor<1.0) reduction_factor=1.0;
  963.     [self setReductionFactor: reduction_factor];
  964.     [self setSelection:first_sample size: number_of_samples];
  965.  
  966.         
  967.     // Step 2:  Move to the right spot
  968.     
  969.     [self scrollToSelection:self];
  970.  
  971.     // Step 3:  Update Information
  972.     [self display];
  973.     [window reenableFlushWindow];
  974.     [window flushWindowIfNeeded];
  975.     return self;
  976.     }
  977.     
  978. - read:(NXTypedStream *)stream
  979.     {
  980.     [super read:stream];
  981.     NXReadTypes(stream,"ifiii",    &display_x_axis_marks,&minor_tick_spacing,
  982.                                 &minor_tick_spacing_format,&major_tick_spacing,
  983.                                 &display_labels);
  984.     play_mark=-1; old_play_mark=-1;
  985.     NXReadTypes(stream,"iiii",    &display_y_axis_grid,&display_zero_line,
  986.                                 &y_display_format,&scroll_to_reflect_playing);
  987.     only_change_play_mark=NO;
  988.     [self _wipeClean];
  989.     return self;
  990.     }
  991.     
  992. - write:(NXTypedStream *)stream
  993.     {
  994.     [super write:stream];
  995.     NXWriteTypes(stream,"ifiii",&display_x_axis_marks,&minor_tick_spacing,
  996.                                 &minor_tick_spacing_format,&major_tick_spacing,
  997.                                 &display_labels);
  998.     NXWriteTypes(stream,"iiii",    &display_y_axis_grid,&display_zero_line,
  999.                                 &y_display_format,&scroll_to_reflect_playing);
  1000.     return self;
  1001.     }
  1002.  
  1003. - awake
  1004.     {
  1005.     id returnval=[super awake];
  1006.     // do nothing for the time being...
  1007.     return returnval;
  1008.     }
  1009.     
  1010. - awakeFromNib
  1011.     {
  1012.     id returnval=[super awakeFromNib];
  1013.     [self adjustBounds:self];
  1014.     return returnval;
  1015.     }
  1016.     
  1017. - toggleXAxisDisplayed:sender
  1018.     {
  1019.     display_x_axis_marks=!display_x_axis_marks;
  1020.     return [self adjustBounds:self];
  1021.     }
  1022.     
  1023. - toggleYAxisDisplayed:sender    
  1024.     {
  1025.     display_y_axis_grid=!display_y_axis_grid;
  1026.     [self _wipeClean];
  1027.     [self display];
  1028.     return self;
  1029.     }
  1030.  
  1031. - toggleLabelsDisplayed:sender
  1032.     {
  1033.     display_labels=!display_labels;
  1034.     [self _wipeClean];
  1035.     [self display];
  1036.     return self;
  1037.     }
  1038.  
  1039. - toggleZeroLineDisplayed:sender
  1040.     {
  1041.     display_zero_line=!display_zero_line;
  1042.     [self _wipeClean];
  1043.     [self display];
  1044.     return self;
  1045.     }
  1046.     
  1047. - toggleScrollToReflectPlaying:sender
  1048.     {
  1049.     scroll_to_reflect_playing=!scroll_to_reflect_playing;
  1050.     return self;
  1051.     }
  1052.  
  1053. - (const char*) getInspectorClassName
  1054.     {
  1055.     return "MiscSoundViewInspector";
  1056.     }
  1057.  
  1058. @end
  1059.